import os
import mmap
import struct
import array

import BSAFontElement

class BSAFont:
    '''Fonts are stored with 95 symbols, representing ASCII 33-127.  The first
    95 bytes are a count of lines used for each symbol, followed by a bunch
    of 16-bit values.  Each 16-bit word is a line of one symbol, in the form
    of a bitmask indicating which pixels on that line should be colored.
    
    There is no width value encoded in the font data.  This must be determined
    by scanning each line of a particular symbol, locating the last (lowest)
    bit on the line.
    
    The symbol data is loaded into the given array as 96 symbols (adding one
    for the space, so no special case is requiring to insert spaces into
    text).  The lines are also stored as 16-bit values, leaving it to the
    rendering code to scan the bits and determine which ones need to be set. 
     
    These are the font files found in the Arena directory:
        ARENAFNT.DAT, height =  9
        CHARFNT.DAT,  height =  8
        FONT_A.DAT,   height = 11
        FONT_B.DAT,   height =  6
        FONT_C.DAT,   height = 14
        FONT_D.DAT,   height =  7
        FONT_S.DAT,   height =  5
        FONT4.DAT,    height =  7
        TEENYFNT.DAT, height =  8'''
    SYMBOL_NUMBER = 96
    
    def __init__(self, filename = '', font_height = 0):
        self._filename = filename
        self._font_height = font_height
        self._symbols = [BSAFontElement.BSAFontElement() \
                         for i in xrange(self.SYMBOL_NUMBER)]

    def __repr__(self):
        return 'BSAFont:filename=[' + self._filename + '],symbols=[' + \
            self._symbols.__repr__() + '],font_height=[' + \
            self._font_height.__repr__() + ']'
                     
    # setters
    def set_filename(self, filename):
        self._filename = filename
    
    def set_symbols(self, symbols):
        self._symbols = symbols
    
    def set_font_height(self, font_height):
        self._font_height = font_height
        
    # getters
    def get_filename(self):
        return self._filename
    
    def get_symbols(self):
        return self._symbols

    def get_font_height(self):
        return self._font_height

    def _open(self):
        self._file = open(self._filename, 'rb')
        size = os.path.getsize(self._filename)
        #self._mmaped_file = mmap.mmap(file.fileno(), size, mmap.MAP_PRIVATE,\
        #                              mmap.PROT_READ)

    def _close(self):
        #self._mmaped_file.close()
        self._file.close()
        
    def load(self):
        self._open()
        
        #self._mmaped_file.seek(95)
        self._file.seek(95)
        
#===============================================================================
#        Special case for the space.  There is no font symbol for it,
#        pad out a symbol for it based upon the font size.
#===============================================================================
        
        self._symbols[0].set_width(4)
        self._symbols[0].set_height(self._font_height)
        
        for i in xrange(1, self.SYMBOL_NUMBER):
            self._symbols[i].set_height(self._font_height)
            
            width = 0
            
            line_num = 0
            while line_num < self._symbols[i].get_height():
                line = self._file.read(2)
                
                self._symbols[i].add_line(line)
                
#===============================================================================
#                 Scan through this line of data to determine how many pixels
#                 are required to draw it.  If this line is longer than any
#                 others, update the width of the symbol.  Once done, we know
#                 exactly how many pixels to render for this symbol, which
#                 also is useful when computing the length of a full line of
#                 text.
#===============================================================================
                mask = 0x8000
                
                for k in xrange(16):
                    tmp_lines = self._symbols[i].get_lines()
                    
                    #tmp = array.array('h', tmp_lines[line_num])
                    line = struct.unpack('H', tmp_lines[line_num])[0]
                    if (line & mask) != 0:
                        if width < (k + 1):
                            width = k + 1
                            
                    mask = mask >> 1
                #- Pad out the width an extra pixel for inter-character spacing.
                width += 1
                
                self._symbols[i].set_width(width)
                line_num += 1
                
        self._close()
        